package cn.taskservice.common.utils.math;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.Objects;

/**
 * 高精度金融计算工具类
 * 支持加、减、乘、除、开平方、幂运算、取整、比较等功能，
 * 满足金融、证券、电商结算等高精度需求场景。
 */
public class FinancialMathUtils{

    private static final int DEFAULT_SCALE = 8; // 默认保留小数点后8位
    private static final RoundingMode DEFAULT_ROUNDING_MODE = RoundingMode.HALF_EVEN; // 银行家舍入法

    private FinancialMathUtils() {}

    /**
     * 高精度加法
     * @param v1 被加数
     * @param v2 加数
     * @param scale 保留小数位数
     * @return 运算结果
     */
    public static BigDecimal add(BigDecimal v1, BigDecimal v2, int scale) {
        checkNull(v1, v2);
        return v1.add(v2).setScale(scale, DEFAULT_ROUNDING_MODE);
    }

    public static BigDecimal add(BigDecimal v1, BigDecimal v2) {
        return add(v1, v2, DEFAULT_SCALE);
    }

    /**
     * 高精度减法
     * @param v1 被减数
     * @param v2 减数
     * @param scale 保留小数位数
     * @return 运算结果
     */
    public static BigDecimal subtract(BigDecimal v1, BigDecimal v2, int scale) {
        checkNull(v1, v2);
        return v1.subtract(v2).setScale(scale, DEFAULT_ROUNDING_MODE);
    }

    public static BigDecimal subtract(BigDecimal v1, BigDecimal v2) {
        return subtract(v1, v2, DEFAULT_SCALE);
    }

    /**
     * 高精度乘法
     * @param v1 被乘数
     * @param v2 乘数
     * @param scale 保留小数位数
     * @return 运算结果
     */
    public static BigDecimal multiply(BigDecimal v1, BigDecimal v2, int scale) {
        checkNull(v1, v2);
        return v1.multiply(v2).setScale(scale, DEFAULT_ROUNDING_MODE);
    }

    public static BigDecimal multiply(BigDecimal v1, BigDecimal v2) {
        return multiply(v1, v2, DEFAULT_SCALE);
    }

    /**
     * 高精度除法
     * @param v1 被除数
     * @param v2 除数
     * @param scale 保留小数位数
     * @return 运算结果
     */
    public static BigDecimal divide(BigDecimal v1, BigDecimal v2, int scale) {
        checkNull(v1, v2);
        if (v2.compareTo(BigDecimal.ZERO) == 0) {
            throw new ArithmeticException("除数不能为零");
        }
        return v1.divide(v2, scale, DEFAULT_ROUNDING_MODE);
    }

    public static BigDecimal divide(BigDecimal v1, BigDecimal v2) {
        return divide(v1, v2, DEFAULT_SCALE);
    }

    /**
     * 高精度开平方，使用牛顿迭代法
     * @param value 被开方数
     * @param scale 保留小数位数
     * @return 平方根结果
     */
    public static BigDecimal sqrt(BigDecimal value, int scale) {
        checkNull(value);
        if (value.compareTo(BigDecimal.ZERO) < 0) {
            throw new ArithmeticException("不能对负数开平方");
        }
        if (value.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO.setScale(scale, DEFAULT_ROUNDING_MODE);
        }
        MathContext mc = new MathContext(scale + 4, DEFAULT_ROUNDING_MODE);
        BigDecimal x0 = BigDecimal.ZERO;
        BigDecimal x1 = new BigDecimal(Math.sqrt(value.doubleValue()), mc);
        while (!x0.equals(x1)) {
            x0 = x1;
            x1 = value.divide(x0, mc);
            x1 = x1.add(x0);
            x1 = x1.divide(BigDecimal.valueOf(2), mc);
        }
        return x1.setScale(scale, DEFAULT_ROUNDING_MODE);
    }

    public static BigDecimal sqrt(BigDecimal value) {
        return sqrt(value, DEFAULT_SCALE);
    }

    /**
     * 幂运算（只支持非负整数）
     * @param base 底数
     * @param exponent 幂指数
     * @param scale 保留小数位数
     * @return 结果
     */
    public static BigDecimal pow(BigDecimal base, int exponent, int scale) {
        checkNull(base);
        if (exponent < 0) throw new IllegalArgumentException("幂指数不能为负数");
        return base.pow(exponent).setScale(scale, DEFAULT_ROUNDING_MODE);
    }

    public static BigDecimal pow(BigDecimal base, int exponent) {
        return pow(base, exponent, DEFAULT_SCALE);
    }

    /**
     * 保留指定位小数，采用默认舍入模式
     * @param value 原始值
     * @param scale 小数位
     * @return 四舍五入后的结果
     */
    public static BigDecimal round(BigDecimal value, int scale) {
        checkNull(value);
        return value.setScale(scale, DEFAULT_ROUNDING_MODE);
    }

    /**
     * 比较两个 BigDecimal 是否相等
     * @param a 数值1
     * @param b 数值2
     * @return 相等返回 true
     */
    public static boolean equals(BigDecimal a, BigDecimal b) {
        checkNull(a, b);
        return a.compareTo(b) == 0;
    }

    /**
     * 比较两个数是否相等（按指定精度）
     * @param a 数值1
     * @param b 数值2
     * @param scale 小数位
     * @return 相等返回 true
     */
    public static boolean equals(BigDecimal a, BigDecimal b, int scale) {
        checkNull(a, b);
        return a.setScale(scale, DEFAULT_ROUNDING_MODE).compareTo(b.setScale(scale, DEFAULT_ROUNDING_MODE)) == 0;
    }

    /**
     * 取绝对值
     * @param value 数值
     * @return 绝对值
     */
    public static BigDecimal abs(BigDecimal value) {
        checkNull(value);
        return value.abs();
    }

    /**
     * 取相反数
     * @param value 数值
     * @return 相反数
     */
    public static BigDecimal negate(BigDecimal value) {
        checkNull(value);
        return value.negate();
    }

    /**
     * 非空校验工具
     * @param values 参数
     */
    private static void checkNull(BigDecimal... values) {
        for (BigDecimal value : values) {
            if (value == null) {
                throw new IllegalArgumentException("参数不能为null");
            }
        }
    }
}